package com.timmy.google_play.manager;

import com.timmy.google_play.bean.ItemBean;
import com.timmy.google_play.conf.Constants;
import com.timmy.google_play.factory.ThreadPoolProxyFactory;
import com.timmy.google_play.utils.CommonUtils;
import com.timmy.google_play.utils.FileUtils;
import com.timmy.google_play.utils.HttpUtils;
import com.timmy.google_play.utils.IOUtils;
import com.timmy.google_play.utils.UIUtils;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;

/**
 * Created by Timmy on 2016/12/15.
 * <p>
 * 下载的管理器,负责和下载相关的逻辑
 * 描    述: 一个应用有一个下载管理器即可
 * 描    述: 1. 定义常量.时刻记录当前的状态
 * 描    述: 2. 根据不同的状态给用户不同的提示
 * 描    述: 3. 属于被观察者,需要时刻发布最新的 DownLoadInfo 给相应的观察者
 */

public class DownLoadManager {

    //单例
    private static DownLoadManager instance;

    //记录用户点击了下载按钮之后产生的 DownLoadInfo 对象
    private Map<String, DownLoadInfo> mCacheDownLoadInfo = new HashMap<>();

    public static final int STATE_UNDOWNLOAD      = 0;      //未下载
    public static final int STATE_DOWNLOADING     = 1;      //下载中
    public static final int STATE_PAUSEDOWNLOAD   = 2;      //暂停下载
    public static final int STATE_WAITINGDOWNLOAD = 3;      //等待下载
    public static final int STATE_DOWNLOADFAILED  = 4;      //下载失败
    public static final int STATE_DOWNLOADED      = 5;      //下载完成
    public static final int STATE_INSTALLED       = 6;      //已安装

    private DownLoadManager() {

    }

    public static DownLoadManager getInstance() {
        if (instance == null) {
            synchronized (DownLoadManager.class) {
                if (instance == null) {
                    instance = new DownLoadManager();
                }
            }
        }

        return instance;
    }

    /**
     * 去下载
     * 用户点击了下载按钮的时候会被调用
     */
    public void downLoad(DownLoadInfo downLoadInfo) {

        mCacheDownLoadInfo.put(downLoadInfo.packageName, downLoadInfo);

        /*================ 当前状态: 未下载 =================*/
        downLoadInfo.curState = STATE_UNDOWNLOAD;

        //downLoadInfo 状态发生了改变,开始同时观察者
        notifyObserver(downLoadInfo);
        /*=============================================*/

        /*================ 当前状态: 等待中 =================*/
        downLoadInfo.curState = STATE_WAITINGDOWNLOAD;

        //downLoadInfo 状态发生了改变,开始同时观察者
        notifyObserver(downLoadInfo);
        /*=============================================*/

        //异步加载
        DownLoadTask task = new DownLoadTask(downLoadInfo);
        downLoadInfo.task = task;
        ThreadPoolProxyFactory.getDownloadThreadPoolProxy().submit(task);

    }


    private class DownLoadTask implements Runnable {

        private final DownLoadInfo downLoadInfo;

        public DownLoadTask(DownLoadInfo downLoadInfo) {
            this.downLoadInfo = downLoadInfo;
        }

        @Override
        public void run() {
            /*================ 当前状态: 下载中 =================*/
            downLoadInfo.curState = STATE_DOWNLOADING;

            //downLoadInfo 状态发生了改变,开始同时观察者
            notifyObserver(downLoadInfo);
            /*=============================================*/

            InputStream  in  = null;
            OutputStream out = null;
            try {
                //apk所对应的文件
                File apkFile   = new File(downLoadInfo.savePath);
                long initRange = 0;

                if (apkFile.exists()) {
                    initRange = apkFile.length();
                }

                //处理初始进度值
                downLoadInfo.progress = initRange;

                //得到数据  --> 发起请求,下载apk
                OkHttpClient okHttpClient = new OkHttpClient();

                //http://localhost:8080/GooglePlayServer/download
                // ?name=app/com.itheima.www/com.itheima.www.apk&range=0

                String url = Constants.URLS.BASEURL + "download";

                Map<String, Object> paramsMap = new HashMap<>();
                paramsMap.put("name", downLoadInfo.downLoadUrl);
                paramsMap.put("range", initRange);   //断点下载的时候需要用到

                //参数map转换为String
                String urlParamsByMap = HttpUtils.getUrlParamsByMap(paramsMap);

                url = url + "?" + urlParamsByMap;

                Request request = new Request.Builder().get().url(url).build();

                //发起请求
                Response response = okHttpClient.newCall(request).execute();

                //处理数据  ---> 把请求回来的流写入的文件当中
                if (response.isSuccessful()) {
                    in = response.body().byteStream();

                    //处理数据  --> 请求回来的流,写入到文件当中

                    out = new FileOutputStream(apkFile, true);      //以追加的方法写文件

                    int     len     = 0;
                    byte[]  buffer  = new byte[1024];
                    boolean isPause = false;

                    while ((len = in.read(buffer)) != -1) {

                        if (downLoadInfo.curState == STATE_PAUSEDOWNLOAD) {
                            isPause = true;
                            break;
                        }

                        downLoadInfo.progress += len;

                        out.write(buffer, 0, len);

                        /*================ 当前状态: 下载中 =================*/
                        downLoadInfo.curState = STATE_DOWNLOADING;

                        //downLoadInfo 状态发生了改变,开始同时观察者
                        notifyObserver(downLoadInfo);
                        /*=============================================*/

                        //如果写完了. --break
                        if (downLoadInfo.curState == downLoadInfo.max) {
                            break;
                        }
                    }

                    if (!isPause) {

                        //下载完成
                        /*================ 当前状态: 下载完成 =================*/
                        downLoadInfo.curState = STATE_DOWNLOADED;

                        //downLoadInfo 状态发生了改变,开始同时观察者
                        notifyObserver(downLoadInfo);
                        /*=============================================*/
                    }

                } else {
                    /*================ 当前状态: 下载失败 =================*/
                    downLoadInfo.curState = STATE_DOWNLOADFAILED;

                    //downLoadInfo 状态发生了改变,开始同时观察者
                    notifyObserver(downLoadInfo);
                    /*=============================================*/
                }
            } catch (IOException e) {
                e.printStackTrace();
                /*================ 当前状态: 下载失败 =================*/
                downLoadInfo.curState = STATE_DOWNLOADFAILED;

                //downLoadInfo 状态发生了改变,开始同时观察者
                notifyObserver(downLoadInfo);
                /*=============================================*/
            } finally {
                IOUtils.close(in);
                IOUtils.close(out);
            }

        }
    }

    /**
     * 根据传递过来的itemBean,返回与之对应的DownLoadInfo对象
     */
    public DownLoadInfo getDownLoadInfo(ItemBean data) {

        DownLoadInfo downLoadInfo = new DownLoadInfo();

        //赋值
        downLoadInfo.downLoadUrl = data.downloadUrl;

        String packageName = data.packageName;              //获取点击的应用程序包名

        String fileName = packageName + ".apk";
        String dir      = FileUtils.getDir("apk");
        File   saveFile = new File(dir, fileName);

        downLoadInfo.savePath = saveFile.getAbsolutePath();
        downLoadInfo.packageName = data.packageName;
        downLoadInfo.max = data.size;


        //最重要的赋值
        downLoadInfo.curState = 0;

        //判断是否安装
        if (CommonUtils.isInstalled(UIUtils.getContext(), packageName)) {
            downLoadInfo.curState = STATE_INSTALLED;
            return downLoadInfo;
        }

        //判断是否下载完成
        if (saveFile.exists() && saveFile.length() == data.size) {
            downLoadInfo.curState = STATE_DOWNLOADED;
            return downLoadInfo;
        }

        //后面就是考虑用户点击了下载按钮
        if (mCacheDownLoadInfo.containsKey(packageName)) {
            DownLoadInfo downLoadInfo_ = mCacheDownLoadInfo.get(packageName);

            return downLoadInfo_;
        }

        //默认就是未下载

        downLoadInfo.curState = STATE_UNDOWNLOAD;
        return downLoadInfo;
    }

    /*================ 自定义观察者设计模式 =================*/
    //1.定义接口以及接口方法
    public interface DownLoadInfoObserver {
        void onDownLoadInfoChange(DownLoadInfo downLoadInfo);
    }

    //2.定义一个集合保存
    List<DownLoadInfoObserver> observers = new ArrayList<>();

    //3.常见方法 -- 添加观察者到观察者集合当中
    public synchronized void addObserver(DownLoadInfoObserver o) {
        if (o == null) {
            throw new NullPointerException();
        } else {
            observers.add(o);
        }
    }

    //3.常见方法 --->  从观察者集合当中移除观察者
    public synchronized void deleteObserver(DownLoadInfoObserver o) {
        observers.remove(o);
    }

    //3.常见方法 ---> 通知所有的观察者消息已经发生了改变
    public void notifyObserver(DownLoadInfo downLoadInfo) {
        for (DownLoadInfoObserver o : observers) {
            o.onDownLoadInfoChange(downLoadInfo);
        }
    }

    /**
     * 暂停下载
     * 正在下载的时候,点击了下载按钮会调用该方法
     */
    public void doPause(DownLoadInfo downLoadInfo) {

        /*================ 改变当前状态,暂停 =================*/
        downLoadInfo.curState = STATE_PAUSEDOWNLOAD;

        //downLoadInfo 状态发生了改变,开始同时观察者
        notifyObserver(downLoadInfo);
        /*==============================================*/
    }

    /**
     * 取消下载
     * 已经有三个apk在下载,当前apk属于等待状态,再次点击,就会取消下载
     */
    public void doCancle(DownLoadInfo downLoadInfo) {

        /*================ 改变当前状态,暂停 =================*/
        downLoadInfo.curState = STATE_UNDOWNLOAD;

        //downLoadInfo 状态发生了改变,开始同时观察者
        notifyObserver(downLoadInfo);
        /*==============================================*/

        //将当前下载的任务移除
        ThreadPoolProxyFactory.getDownloadThreadPoolProxy().remove(downLoadInfo.task);
    }
}
